package com.fy56.platform.logistics.utils;

import org.apache.commons.io.IOUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * 异步请求处理工具类
 */
public class HttpAsyncUtil {

    //连接超时
    private static final int CONN_TIMEOUT = 300000;

    //最大总连接值
    private static final int SOCKET_TIMEOUT = 300000;

    //最大总连接值
    private static final int MAX_CONN_TOTAL = 100;

    //每个路由的最大连接值
    private static final int MAX_CONN_PERROUTE = 100;

    public static String asyncMutiPost(String url, String body, Map<String, String> headers){
        return doAsyncPost(getHttpPost(url,body,headers),CONN_TIMEOUT,SOCKET_TIMEOUT,MAX_CONN_TOTAL,MAX_CONN_PERROUTE);
    }

    public static String asyncMutiPost(String url, Map<String, String> body, Map<String, String> headers){
        return doAsyncPost(getHttpPost(url,body,headers),CONN_TIMEOUT,SOCKET_TIMEOUT,MAX_CONN_TOTAL,MAX_CONN_PERROUTE);
    }

    public static String asyncPost(String url, Map<String, String> body, Map<String, String> headers){
        return doAsyncPost(getHttpPost(url,body,headers));
    }

    private static CloseableHttpAsyncClient getCloseableHttpAsyncClient(Integer connectTimeout,Integer socketTimeout,
                                                                 Integer connTotal,Integer connPerRoute){
        connectTimeout = connectTimeout == null ? CONN_TIMEOUT : connectTimeout;
        socketTimeout = socketTimeout == null ? SOCKET_TIMEOUT : socketTimeout;
        connTotal = connTotal == null ? MAX_CONN_TOTAL : connTotal;
        connPerRoute = connPerRoute == null ? MAX_CONN_PERROUTE : connPerRoute;
        //setConnectTimeout（默认值：-1）参数解析：0不限超时，负值为系统默认
        //setSocketTimeout（默认值：-1）参数解析：0不限超时，负值为系统默认
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(connectTimeout)
                .setConnectTimeout(socketTimeout).build();
        return HttpAsyncClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setMaxConnTotal(connTotal)
                .setMaxConnPerRoute(connPerRoute)
                .build();
    }

    private static String doAsyncPost(HttpPost httpPost){
        final CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
        httpClient.start();
        final Future future = httpClient.execute(httpPost, null);
        HttpResponse response = null;
        try {
            response = (HttpResponse) future.get();
            return IOUtils.toString(response.getEntity().getContent(), "UTF-8");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            httpPost.releaseConnection();
            try {
                httpClient.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    private static String doAsyncPost(HttpPost httpPost,Integer connectTimeout,Integer socketTimeout,
                                      Integer connTotal,Integer connPerRoute){
        String result = null;
        final CloseableHttpAsyncClient httpclient = getCloseableHttpAsyncClient(
                connectTimeout,socketTimeout,connTotal,connPerRoute);
        try {
            httpclient.start();
            final CountDownLatch latch = new CountDownLatch(1);
            httpclient.execute(httpPost, new FutureCallback<HttpResponse>() {

                @Override
                public void completed(final HttpResponse response) {
                    latch.countDown();
                }

                @Override
                public void failed(final Exception ex) {
                    latch.countDown();
                }

                @Override
                public void cancelled() {
                    latch.countDown();
                }

            });
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static HttpPost getHttpPost(String url, Map<String, String> params, Map<String, String> headers){
        HttpPost post = new HttpPost(url);
        if (params != null && !params.isEmpty()) {
            List<NameValuePair> formParams = new ArrayList<NameValuePair>();
            Set<Map.Entry<String, String>> entrySet = params.entrySet();
            for (Map.Entry<String, String> entry : entrySet) {
                formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
            ContentType mode = ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8);
            entity.setContentType(mode.toString());
            post.setEntity(entity);
        }

        setHeader(post,headers);
        return post;
    }

    private static HttpPost getHttpPost(String url, String body, Map<String, String> headers){
        HttpPost post = new HttpPost(url);
        if (StringUtil.isNotBlank(body)) {
            HttpEntity entity = new StringEntity(body, ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8));
            post.setEntity(entity);
        }
        setHeader(post,headers);
        return post;
    }

    private static void setHeader(HttpPost post,Map<String, String> headers){
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }
}
